package beanFactory;

import Utils.StringUtils;
import beanDefinition.BeanDefinition;
import beanDefinition.MultablePropertyValues;
import beanDefinition.PropertyValue;
import beanDefinitionReader.BeanDefinitionReader;
import beanDefinitionReader.XmlBeanDefinitionReader;
import beanDefinitionRegistry.BeanDefinitionRegistry;

import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class ClassPathXmlApplicationContext extends AbstractApplicationContext{

  public ClassPathXmlApplicationContext(String configLocation)
   {
       //配置文件的路径
       this.configLocation=configLocation;
       //创建解析器对象
       beanDefinitionReader=new XmlBeanDefinitionReader();
       //调用父类中的refresh方法
       try {
           refresh();
       } catch (Exception e) {
           e.printStackTrace();
       }
   }
   //根据bean对象的名称获取bean对象
    @Override
    public Object getBean(String name) throws Exception {
        //判断对象容器中是否包含指定名称的bean对象，如果包含，直接返回即可
        //如果不包含，需要自行创建

        //尝试从容器中获取指定bean对象
        Object o = signletonObjects.get(name);
        //容器中存在该对象
        if(o!=null)
        {
            return o;
        }
        //获取BeanDefinition对象
        //1.先获取注册表对象
        BeanDefinitionRegistry beanDefinitionRegistry=beanDefinitionReader.getRegistry();
        //2.从注册表中获取对应的BeanDefinition对象
        BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(name);
        //如果注册表中也没有对应的BeanDefinition对象，那么抛出异常
        if(beanDefinition==null)
            return null;
        //3.获取bean信息中的className:对象的全类名
        String className = beanDefinition.getClassName();
        //4.通过反射创建对象
        Class<?> clazz = Class.forName(className);
        //获得了实例化完后的对象
        Object beanObj=clazz.newInstance();

        //进行依赖注入操作---创建完对象后的属性赋值
        //先获取所有property对象--每一个里面property对象封装了对象的一个属性的名字和对应的值
        MultablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        //获取迭代器，遍历存放property对象的list集合
        Iterator<PropertyValue> iterator = propertyValues.iterator();
        //使用迭代器遍历
        while(iterator.hasNext())
        {
            //获取name属性值
            PropertyValue next = iterator.next();
            String propertyName = next.getName();
            //获取value属性
            String propertyValue = next.getValue();
            //获取ref属性
            String propertyRef = next.getRef();
            //ref和value只能出现一个
            if(propertyRef!=null&&!"".equals(propertyRef))
            {
                //ref属性存在
                //获取依赖的bean对象---使用递归
                Object bean = getBean(propertyRef);
                //拼接方法名---传入属性名，获取其对应的set方法的名字
                String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
                //获取所有的方法对象
                Method[] methods=clazz.getMethods();
                //找到我们需要执行的对象属性的set方法
                for (Method method : methods) {
                    if(methodName.equals(method.getName()))
                    {
                        //执行该setter方法
                        //这里是对依赖对象进行属性注入
                        //例如:
                        // class Person
//                        {
//                            Stu stu
//                        }
                        method.invoke(beanObj,bean);
                    }
                }
            }
            //value属性存在
            if(propertyValue!=null&&!"".equals(propertyValue))
            {
                //拼接方法名--传入属性名，获取对应的set方法
                String methodName1= StringUtils.getSetterMethodByFieldName(propertyName);
                //获取method对象,方法名称，和参数类型，这里参数我们针对的是String类型进行操作
                Method method = clazz.getMethod(methodName1, String.class);
                //执行哪个对象的方法，实参的值
                method.invoke(beanObj,propertyValue);
            }

        }

        //返回beanObj对象之前，将该对象存储到map容器中
        signletonObjects.put(name,beanObj);
        return beanObj;
  }

    //根据bean对象的名称获取bean对象，并进行强制类型转换
    @Override
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
       //首先调用上面的getBean方法得到一个bean对象
        Object bean = getBean(name);
        //如果为null，说明没有这个对象
        if(bean==null)
            return null;
        //进行类型强转，并返回强转后的对象
        return clazz.cast(bean);
    }
}
